#include "apptencentapi.h"
#include "apptencentapiconfig.h"
#include "CTTimeEscapeCheck.h"
#include "tool/CTSha1.h"
#include "net/CTBase64.h"

CTLIB_SO_FUNC(CAppTencentApi)

static const char CHAR2HEX[16] =
{
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};

CAppTencentApi* CAppTencentApi::m_pstApiCtrl = NULL;
CAppTencentApi::CAppTencentApi()
{
    m_pstApiCtrl = this;
}

CAppTencentApi::~CAppTencentApi()
{

}

int CAppTencentApi::Init(void *pData)
{
    CT_ASSERT(pData);
    CT_TRACE(("begin load config ok"));
    CONF->LoadConfig((const char*)pData);
    CT_TRACE(("so load config ok"));

    int iRet = 0;
    iRet = curl_global_init(CURL_GLOBAL_ALL);
    if (iRet)
    {
        CT_ERROR(("init curl error"));
        return -1;
    }

    return RET_OK;
}

size_t CAppTencentApi::RecvPacket(char *ptr, size_t size, size_t nmemb, void *userdata)
{
    if (size * nmemb >= CTLib::CTKeyValueData::MAX_KEY_VALUE_LEN)
    {
        CT_ERROR(("%d", size * nmemb));
        return 0;
    }

    //CT_TRACE(("rev %s", ptr));

    if (CAppTencentApi::m_pstApiCtrl->m_iRecvPacketLen + size * nmemb + 1 >= sizeof(CAppTencentApi::m_pstApiCtrl->m_szRecvPacketBuf))
    {
        CT_ERROR(("msg too larger, %d, %d", strlen(CAppTencentApi::m_pstApiCtrl->m_szRecvPacketBuf), size * nmemb));
        return 0;
    }

    CT_TRACE(("recv %d", size * nmemb));
    memcpy(CAppTencentApi::m_pstApiCtrl->m_szRecvPacketBuf + CAppTencentApi::m_pstApiCtrl->m_iRecvPacketLen, ptr, size * nmemb);
    CAppTencentApi::m_pstApiCtrl->m_iRecvPacketLen += size * nmemb;

    /*
    iRet = CAppTencentApi::m_pstApiCtrl->m_stAnsApi._stKeyValue.ParseFromJson(ptr);
    if (iRet <= 0)
    {
        CT_ERROR(("parse from json error, ret %d", iRet));
        return 0;
    }
    */

    return size * nmemb;
}

int CAppTencentApi::InitCurl()
{       
    m_pstCurlHandle = curl_easy_init();
    if (NULL == m_pstCurlHandle)
    {
        CT_ERROR(("init curl handle error"));
        return -2;
    }

    if (IsApiUseGet())
    {
        curl_easy_setopt(m_pstCurlHandle, CURLOPT_WRITEFUNCTION, CAppTencentApi::RecvPacket);
        curl_easy_setopt(m_pstCurlHandle, CURLOPT_HTTPGET, 1);
    }
    else
    {
        curl_easy_setopt(m_pstCurlHandle, CURLOPT_NOPROGRESS, 1);
        curl_easy_setopt(m_pstCurlHandle, CURLOPT_WRITEFUNCTION, CAppTencentApi::RecvPacket);
        curl_easy_setopt(m_pstCurlHandle, CURLOPT_HTTPPOST, 1);
    }
        
    return 0;
}

int CAppTencentApi::FreeCurl()
{
    if (m_pstCurlHandle)
    {
        curl_easy_cleanup(m_pstCurlHandle);
    }
    
    return 0;
}

int CAppTencentApi::Do(const CEnpNetHead& stHead, const char* pszReqBuf, int iReqLen, 
                  char* pszAnsBuf, int iAnsBufLen, int& iAnsLen)
{
    CT_TRACE(("RecvMsg: len=%d msg=%s AnsBufLen=%d", iReqLen, pszReqBuf, iAnsBufLen));

    int iRet = InitReq(pszReqBuf, iReqLen);
    if (iRet != RET_OK)
    {
        SendAns(iRet, pszAnsBuf, iAnsBufLen, iAnsLen);
    }

    iRet = DealReq();
    SendAns(iRet, pszAnsBuf, iAnsBufLen, iAnsLen);
    return iRet;
}

int CAppTencentApi::Exit()
{
    curl_global_cleanup();
    return RET_OK;
}

int CAppTencentApi::InitReq(const char* pszReqBuf, int iReqLen)
{
    m_stCodeStream.Attach((char*)pszReqBuf, (unsigned short)(iReqLen & 0xFFFF));
    m_stReqApi._stKeyValue.Clear();
    CodeConvert(m_stCodeStream, m_stMsgHead, NULL, bin_decode());
    CodeConvert(m_stCodeStream, m_stReqApi, NULL, bin_decode());

    CT_TRACE(("get req: api name: %s", m_stReqApi._szApiName()));

    if (m_stCodeStream.m_iRet)
    {
        CT_WARNING(("CodeConvert failed, ret=%d", m_stCodeStream.m_iRet));
        return RET_INVALID_PARAM;
    }

    if (m_stMsgHead.shMsgID != ID_INTER_API_TENCENT || m_stMsgHead.shMsgType != EMT_REQUEST)
    {
        CT_WARNING(("msghead(%d, %d) invalid", m_stMsgHead.shMsgID, m_stMsgHead.shMsgType));
        return RET_INVALID_PARAM;
    }

    CT_TRACE(("init req ok"));
    return RET_OK;
}

int CAppTencentApi::DealReq()
{
    int iRet = 0;
    InitCurl();

    if (m_stReqApi._szApiName.GetDataLen() == 0)
    {
        CT_ERROR((""));
        return RET_SYS_ERROR;
    }

    memset(m_szHttpReqEncodeBuf, 0x0, sizeof(m_szHttpReqEncodeBuf));
    memset(m_szSig, 0x0, sizeof(m_szSig));

    //构造请求串
    char szHttpReq[40960] = {0};
    char szTmp[40960] = {0};
    
    //生成SIG
    const char* sig = GetSig();
    if (NULL == sig)
    {
        CT_ERROR((""));
        FreeCurl();
        return -1;
    }

    for (int i = 0; i < m_stReqApi._stKeyValue._astKeyValueData.Size(); ++i)
    {
        if (m_stReqApi._stKeyValue._astKeyValueData[i]._ucIsHeadData != 1)
        {
            continue;
        }

        char szTmp2[10240] = {0};
        sprintf(szTmp2, "%s%s=%s", (i == 0 ? "" : "&"), 
            m_stReqApi._stKeyValue._astKeyValueData[i]._szKey(),
            UrlEncode(m_stReqApi._stKeyValue.GetValue(m_stReqApi._stKeyValue._astKeyValueData[i]._szKey())));

        strcat(szTmp, szTmp2);
    }

    char szTmp2[1024] = {0};
    sprintf(szTmp2, "&sig=%s", sig);
    strcat(szTmp, szTmp2);

    unsigned short ApiPort;
    char ApiHost[32];

    switch(m_stReqApi._ucApiType)
    {
    case CReqTencentApi::COMPASS:
        {
            ApiPort = CONF->CompassApiPort;
            sprintf(ApiHost, "%s", CONF->CompassApiHost);
        }
        break;
    default:
        ApiPort = CONF->ApiPort;
        sprintf(ApiHost, "%s", CONF->ApiHost);
    }

    char szProto[100];
    if (IsApiUseHttps())
    {
        sprintf(szProto, "https");
    }
    else
    {
        sprintf(szProto, "http");
    }

    if (ApiPort == 0)
    {
        sprintf(szHttpReq, "%s://%s%s", szProto, ApiHost, m_stReqApi._szApiName());
    }
    else
    {
        sprintf(szHttpReq, "%s://%s:%d%s", szProto, ApiHost, ApiPort, m_stReqApi._szApiName());
    }
    
    if (IsApiUseGet())
    {
        //使用GET方式
        char szUrlGet[40960] = {0};
        sprintf (szUrlGet, "%s?%s", szHttpReq, szTmp);
        
        curl_easy_setopt(m_pstCurlHandle, CURLOPT_URL, szUrlGet);
        
        CT_TRACE(("http req: %s", szUrlGet));
    }
    else
    {    
        curl_easy_setopt(m_pstCurlHandle, CURLOPT_URL, szHttpReq);
        curl_easy_setopt(m_pstCurlHandle, CURLOPT_SSL_VERIFYPEER, false);
        curl_easy_setopt(m_pstCurlHandle, CURLOPT_SSL_VERIFYHOST, false);
        curl_easy_setopt(m_pstCurlHandle, CURLOPT_POSTFIELDS, szTmp);

        CT_TRACE(("http req: %s", szHttpReq));
        CT_TRACE(("http para: %s", szTmp));
    }

    m_iRecvPacketLen = 0;
    m_szRecvPacketBuf[0] = '\0';
    iRet = curl_easy_perform(m_pstCurlHandle);
    if (iRet != 0)
    {
        CT_ERROR(("curl_easy_perform ret %d\n", iRet));
        FreeCurl();
        return -3;
    }
    m_szRecvPacketBuf[m_iRecvPacketLen] = '\0';

    //解析数据
    CT_TRACE(("msg len %d", m_iRecvPacketLen));
    m_stAnsApi._stKeyValue.Clear();
    iRet = m_stAnsApi._stKeyValue.ParseFromJson(m_szRecvPacketBuf);
    if (iRet <= 0)
    {
        CT_ERROR(("parse from json error, ret %d", iRet));
        CT_ERROR(("buf: \n %s", m_szRecvPacketBuf));
        return -4;
    }

    //copy数据
    //m_stAnsApi = CAppTencentApi::m_pstApiCtrl->m_stAnsApi;

    CTLib::CTKeyValue& stTApiKeyValue = m_stAnsApi._stKeyValue;
    CT_TRACE(("rev key count = %d", stTApiKeyValue._astKeyValueData.Size()));
    for (int i = 0; i < stTApiKeyValue._astKeyValueData.Size(); ++i)
    {
        if (stTApiKeyValue._astKeyValueData[i]._ucIsHeadData != 1)
        {
            continue;
        }
        
        CT_TRACE(("%s=%s", stTApiKeyValue._astKeyValueData[i]._szKey(), stTApiKeyValue.GetValue(stTApiKeyValue._astKeyValueData[i]._szKey())));
    }

    FreeCurl();

    return RET_OK;
}

//生成签名
const char* CAppTencentApi::GetSig()
{   
    //构造原串
    char szTmp1[40960] = {0};
    char szURI[40960] = {0};
    char szURIContent[40960] = {0};
    char szKey[50] = {0};

    memset(m_szSig, 0x0, sizeof(m_szSig));
    
    //将请求的URI路径进行URL编码
    const char *url = UrlEncode(m_stReqApi._szApiName());
    if (NULL == url)
    {
        return NULL;
    }

    sprintf(szURI, "%s", url);

    //将除“sig”外的所有参数按key进行字典升序排列
    m_stReqApi._stKeyValue.OrderByLetter();

    //将排序后的参数(key=value)用&拼接起来
    szTmp1[0] = '\0';
    for (int i = 0; i < m_stReqApi._stKeyValue._astKeyValueData.Size(); ++i)
    {
        if (m_stReqApi._stKeyValue._astKeyValueData[i]._ucIsHeadData != 1)
        {
            continue;
        }

        char szTmp2[40960];
        sprintf(szTmp2, "%s%s=%s", (i == 0 ? "" : "&"), 
            m_stReqApi._stKeyValue._astKeyValueData[i]._szKey(),
            m_stReqApi._stKeyValue.GetValue(m_stReqApi._stKeyValue._astKeyValueData[i]._szKey()));
        strcat(szTmp1, szTmp2);
    }

    //进行URL编码
    CT_TRACE(("%s", szTmp1));
    sprintf(szURIContent, "%s", UrlEncode(szTmp1));

    //将HTTP请求方式，第1步以及第3步中的到的字符串用&拼接起来        
    if (IsApiUseGet())
    {
        sprintf(m_szHttpReqEncodeBuf, "%s&%s&%s", "GET", szURI, szURIContent);
    }    
    else
    {
        sprintf(m_szHttpReqEncodeBuf, "%s&%s&%s", "POST", szURI, szURIContent);
    }

    //构造密钥
    sprintf(szKey, "%s&", CONF->ApiSecKey);

    //使用HMAC-SHA1加密算法
    CT_TRACE(("before HMAC-SHA1 : %s, %d, %s, %d", m_szHttpReqEncodeBuf, strlen(m_szHttpReqEncodeBuf), szKey, strlen(szKey)));
    SHA1Encode(m_szHttpReqEncodeBuf, strlen(m_szHttpReqEncodeBuf), 
        szKey, strlen(szKey),
        m_szSig, sizeof(m_szSig));

    //base 64
    char sig[50];
    memcpy(sig, m_szSig, 20);
    CTBase64::Encode(sig, 20, m_szSig);

    const char* pTmp = UrlEncode(m_szSig);
    memcpy(m_szSig, pTmp, strlen(pTmp));    

    return m_szSig;
}

const char* CAppTencentApi::UrlEncode(const char* szStr)
{
    if (szStr == NULL)
    {
        return NULL;
    }

    if (strlen(szStr) >= sizeof(m_szHttpReqEncodeBuf) / 3)
    {
        return NULL;
    }

    memset(m_szHttpReqEncodeBuf, 0x0, sizeof(m_szHttpReqEncodeBuf));

    int j = 0;
    for (size_t i = 0; i < strlen(szStr); ++i)
    {
        char c = (unsigned char)szStr[i];
        if (c == '.' || c == '_' || c == '-' 
            || (c >= 48 && c <= 57)     //数字
            || (c >= 65 && c <= 90)     //大写字母
            || (c >= 97 && c <= 122))   //小写字母
        {
            m_szHttpReqEncodeBuf[j++] = c;
            continue;
        }

        char h = ((unsigned char) c) >> 4;
        char l = ((unsigned char) c) & 0xF;

        m_szHttpReqEncodeBuf[j++] = '%';
        m_szHttpReqEncodeBuf[j++] = CHAR2HEX[(int)h];
        m_szHttpReqEncodeBuf[j++] = CHAR2HEX[(int)l];
    }

    return m_szHttpReqEncodeBuf;
}

int CAppTencentApi::SendAns(int iResult, char* pszAnsBuf, int iAnsBufLen, int& iAnsLen)
{
    m_stAnsApi._stResult.iResultID = iResult;

    CT_TRACE(("begin send ans, ret = %d", iResult));
    unsigned short ushAnsBufLen = (unsigned short)(iAnsBufLen & 0xFFFF);
    int iRet = m_stCodeStream.Attach(pszAnsBuf, ushAnsBufLen);
    if (iRet != RET_OK)
    {
        return iRet;
    }

    m_stMsgHead.shMsgType = EMT_ANSWER;
    CodeConvert(m_stCodeStream, m_stMsgHead, NULL, bin_encode());
    CodeConvert(m_stCodeStream, m_stAnsApi, NULL, bin_encode());
    iAnsLen = m_stCodeStream.m_ushPos;

    if(iRet)
    {
        CT_WARNING(("SendAns error"));
    }

    CT_TRACE(("send ans ok, anslen = %d", iAnsLen));
    return iRet;
}

bool CAppTencentApi::IsApiUseGet ()
{
    if (0 == strcmp("/v3/pay/check_task_status", m_stReqApi._szApiName()))
    {
        return true;
    }

    return false;
}


bool CAppTencentApi::IsApiUseHttps ()
{
    if (0 == strcmp("/v3/pay/inform_task_completed", m_stReqApi._szApiName())
        || 0 == strcmp("/v3/pay/buy_goods", m_stReqApi._szApiName())
        || 0 == strcmp("/v3/pay/confirm_delivery", m_stReqApi._szApiName()))
    {
        return true;
    }

    return false;
}
